package com.baba.vote.service.impl;

import com.baba.vote.common.VoteErrorCode;
import com.baba.vote.common.VoteException;
import com.baba.vote.constant.VoteConstant;
import com.baba.vote.constant.VoteSceneConstant;
import com.baba.vote.dao.VoteActMapper;
import com.baba.vote.dao.VoteItemMapper;
import com.baba.vote.dao.VotePeopleMapper;
import com.baba.vote.dto.request.VoteActBo;
import com.baba.vote.dto.request.VoteItemBo;
import com.baba.vote.dto.request.VotePeopleBo;
import com.baba.vote.dto.response.VoteActDto;
import com.baba.vote.dto.response.VoteItemDto;
import com.baba.vote.logic.VoteLogic;
import com.baba.vote.pojo.VoteAct;
import com.baba.vote.pojo.VoteItem;
import com.baba.vote.pojo.VotePeople;
import com.baba.vote.service.FileService;
import com.baba.vote.service.VoteService;
import com.baba.vote.util.Locationutil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author moxinHuoHuo
 * @create 2020/4/7 下午11:45
 */
@Service
@Slf4j
public class VoteServiceImpl implements VoteService {

  @Autowired
  private VoteActMapper voteActMapper;

  @Autowired
  private VoteItemMapper voteItemMapper;

  @Autowired
  private VotePeopleMapper votePeopleMapper;

  @Autowired
  private FileService fileService;

  @Autowired
  private VoteLogic voteLogic;

  @Value("${base-url}")
  private String baseUrl;


  @Override
  @Transactional(rollbackFor = Exception.class)
  public VoteActDto add(VoteActBo voteActBo) {

    // 0. 验证是否有投票活动，有则不能开启第二个
    VoteActDto detail = detail();
    if (detail != null) {
      // 删除投票活动
      delete();
    }

    // 1. 先添加活动
    VoteAct voteAct = voteActBo.doForward();
    voteActMapper.insert(voteAct);

    // 2. 添加活动选项
    List<VoteItemBo> voteItemBoList = voteActBo.getVotingContents();
    List<VoteItem> voteItemList = voteItemBoList.stream()
        .map(e -> e.doForward(voteAct.getId()))
        .collect(Collectors.toList());
    for (VoteItem voteItem : voteItemList) {
      voteItemMapper.insertSelective(voteItem);
    }

    // 3. 为投票结果预设一张图片
    initResultImg(voteAct);

    // 4. 创建活动对应的投票入口二维码、投票结果二维码
    createQRcode(voteAct);


    return detail();
  }

  private void initResultImg(VoteAct voteAct) {

    String resultFileName = VoteConstant.VOTE_RESULT_FRONT + "_" + voteAct.getId() + ".jpg";
    InputStream inputStream = getReSourceFile("static/template.jpg");

    String ossResult = fileService.uploadPictureAliOss(inputStream, resultFileName);
    voteAct.setResultImg(ossResult);
    voteActMapper.updateByPrimaryKeySelective(voteAct);
  }

  @Override
  public VoteActDto detail() {

    List<VoteActDto> dto = voteActMapper.getDto();

    if (dto.isEmpty()) {
      return null;
    }

    VoteActDto voteActDto = dto.get(0);
    voteActDto.setVoted(0);
    voteActDto.setJoinQRCode(voteActDto.getQrcodeEntranceImg());
    voteActDto.setDownloadQRCode(voteActDto.getQrcodeResultImg());

    List<VoteItemDto> votingContents = voteActDto.getVotingContents();
    votingContents.forEach(VoteItemDto::initSupportNum);

    for (VoteItemDto e: votingContents) {
      voteActDto.setVoted(voteActDto.getVoted() + e.getSupportNum());
    }

    return voteActDto;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public String uploadVoteResult(MultipartFile file) {

    // 判断有没有投票活动，没有投票上传图片失败
    VoteAct voteAct = get();

    String fileName = VoteConstant.VOTE_RESULT_FRONT + "_" + voteAct.getId();
    String result = fileService.upload(file, VoteConstant.SAVE_PATH, fileName);
    // 将图片上传到oss
    String ossResult = fileService.uploadPictureAliOss(result);
    // 删除本地文件
    fileService.deleteLocalFile(result);

    // 更新活动存储图片的地址
    voteAct.setResultImg(ossResult);
    voteActMapper.updateByPrimaryKeySelective(voteAct);

    return result;

  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public Boolean stop() {

    VoteActDto detail = Optional.ofNullable(detail())
        .orElseThrow(() -> new VoteException(VoteErrorCode.VOTE_NOT_EXIST));

    voteActMapper.updateStatusById(false, detail.getId());

    return true;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public Boolean delete() {

    VoteActDto detail = Optional.ofNullable(detail())
        .orElseThrow(() -> new VoteException(VoteErrorCode.VOTE_NOT_EXIST));

    // 1. 删除活动
    voteActMapper.deleteByPrimaryKey(detail.getId());

    // 2. 删除附加项
    voteItemMapper.deleteByVoteActId(detail.getId());

    // 3. 删除投票人
    votePeopleMapper.deleteByVoteActId(detail.getId());

    // 4. 删除图片
    fileService.deletePictureAliOss(detail.getQrcodeEntranceImg());
    fileService.deletePictureAliOss(detail.getQrcodeResultImg());
    if (detail.getResultImg() != null) {
      fileService.deletePictureAliOss(detail.getResultImg());
    }

    return true;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public Boolean submission(Integer voteActId, Integer voteItemId, VotePeopleBo votePeopleBo) {

    log.info("选项id:{}，投票人信息:{}====", voteItemId, votePeopleBo);

    VoteActDto detail = Optional.ofNullable(detail())
        .orElseThrow(() -> new VoteException(VoteErrorCode.VOTE_NOT_EXIST));

    // 验证数据合理性
    if (voteItemMapper.countByIdAndVoteActId(voteItemId, detail.getId()) <= 0) {
      throw new VoteException("voteItemId与voteActId不是组合关系");
    }

    // 1. 判断该场次投票是否已经关闭
    if (!detail.getStatus()) {
      throw new VoteException(VoteErrorCode.VOTE_CLOSE);
    }

    // 2. 验证该场次投票是否达到限定人数
    Integer peopleNum = detail.getPeopleNum();
    if (peopleNum != 0) {
      Integer count = votePeopleMapper.countByVoteActId(detail.getId());
      if (count >= detail.getPeopleNum()) {
        throw new VoteException(VoteErrorCode.VOTE_OVERFLOW);
      }
    }

    // 3. 验证唯一标识是否进行了投票
    VotePeople check = votePeopleMapper.findByUidAndVoteActId(votePeopleBo.getUid(), detail.getId());
    if (check != null) {
      throw new VoteException(VoteErrorCode.VOTE_HAVE_VOTED);
    }

    // 4. 如果是在场投票，则验证是否超过现场投票有限范围
    if ((detail.getType() & VoteSceneConstant.LOCATION) != 0) {
      // 计算两地的距离
      double distance = Locationutil.getDistance(detail.getLatitude(), detail.getLongitude(), votePeopleBo.getLatitude(), votePeopleBo.getLongitude());

      if (distance > VoteSceneConstant.EFFECTIVE_DISTANCE) {
        throw new VoteException(VoteErrorCode.VOTE_OUT_VALID_RANGE);
      }
    }

    // 插入数据
    VotePeople votePeople = votePeopleBo.doForward(voteItemId, detail.getId());
    votePeopleMapper.insertSelective(votePeople);

    // 投完票之后，验证人数是否满了，满的话修改状态
    Integer count = votePeopleMapper.countByVoteActId(detail.getId());
    if (count >= detail.getPeopleNum() && detail.getPeopleNum() != 0) {
      stop();
    }

    return true;
  }

  @Override
  public VoteAct get() {

    List<VoteAct> voteActList = voteActMapper.get();
    if (voteActList.isEmpty()) {
      throw new VoteException(VoteErrorCode.VOTE_NOT_EXIST);
    }

    return voteActList.get(0);
  }

  private void createQRcode(VoteAct voteAct) {

    String qrcodeEntranceImg = voteLogic.createQrcodeEntranceImg(voteAct.getId());
    String qrcodeResultImg = voteLogic.createQrcodeResultImg(voteAct);
    // 将图片上传oss
    String entranceImg = fileService.uploadPictureAliOss(qrcodeEntranceImg);
    String resultImg = fileService.uploadPictureAliOss(qrcodeResultImg);
    // 删除本地图片
    fileService.deleteLocalFile(qrcodeEntranceImg);
    fileService.deleteLocalFile(qrcodeResultImg);

    voteAct.setQrcodeEntranceImg(entranceImg);
    voteAct.setQrcodeResultImg(resultImg);

    voteActMapper.updateByPrimaryKeySelective(voteAct);
  }

  private InputStream getReSourceFile(String fileName) {

    InputStream stream = null;
    try {
      stream = getClass().getClassLoader().getResourceAsStream(fileName);

    } catch (Exception e) {
      e.printStackTrace();
    }

    return stream;
  }

}
